#include "Properties.h"
#include <stdio.h>
#include <conio.h>
#include <malloc.h>
#include <memory.h>
#include "..\Utils\Utils.h"
#include "..\List\LIST.h"
#include "..\Clients\Clients.h"

#pragma warning (disable: 4996)
#pragma warning (disable: 4090)

//Function that input property information from user, return allocated pointer.
DemandProperty * ReadPropertyFromUser()
{
	DemandProperty *D;
	
	D = (DemandProperty*)malloc(sizeof(DemandProperty));

	strcpy(D->PropertyRecord.PropertyCity,"");
	strcpy(D->PropertyRecord.Contacts[0].FullName,"");
	strcpy(D->PropertyRecord.Contacts[0].Phone,"");
	strcpy(D->PropertyRecord.Contacts[1].FullName,"");
	strcpy(D->PropertyRecord.Contacts[1].Phone,"");
	
		if ((!Client_Insert_Integer(&(D->PropertyRecord.PropertyPrice),9,"Enter Price: "))
			||
			(!Client_Insert_Byte(&(D->PropertyRecord.PropertyRooms),2,"Enter Rooms: "))
		    ||
			(!Client_Insert_Between(&(D->PropertyRecord.PropertyKind),DPKindStrings,DPKindLength,"Select Kind:"))
			||
			(!Client_Insert_Between(&(D->PropertyRecord.PropertyType),DPPropertyTypeStrings,ActiveStringsLength,"Select Type:"))
			||
			(!Client_Insert_String(D->PropertyRecord.PropertyCity,"Enter City: ",20,TEXT_S))		
			||
			(!Client_Insert_Between(&D->PropertyRecord.PropertyActive,ActiveStrings,ActiveStringsLength,"Select Property Activation:"))
			||
			(!Client_Insert_String(D->PropertyRecord.Contacts[0].FullName,"Enter Contact 1 Name: ",20,TEXT_S))		
			||
			(!Client_Insert_String(D->PropertyRecord.Contacts[0].Phone,"Enter Contact 1 Phone: ",20,NUM_S)))		
		{
			free(D);
			return NULL;
		}

		Client_Insert_String(D->PropertyRecord.Contacts[1].FullName,"Enter Contact 2 Name: ",20,TEXT_S);				
		Client_Insert_String(D->PropertyRecord.Contacts[1].Phone,"Enter Contact 2 Phone: ",20,NUM_S);		

		return D;
}
//Function that adds new property, call to ReadPropertyFromUser, adds to list.
void Add_New_Property(List *PropertiesList,int *PropertiesCount)
{
Position PO;
DemandProperty *D;
	if (*PropertiesCount==0)
		*PropertiesList = MakeEmpty(NULL);

	PO = Header(*PropertiesList);

	D = ReadPropertyFromUser();
	if (D==NULL)
		ConfirmationMessage("User Aborted.");
	else
	{
		Insert(*D,*PropertiesList,PO,EProperty);
		(*PropertiesCount)++;
		ConfirmationMessage("Property Added.");
	}

}
//Function show page with all properties, navigation also exist.
void Show_All_Properties(List *PropertiesList,int *PropertiesCount)
{
char ch,ind=0;
Position PO;
if (*PropertiesCount==0)
{
	ConfirmationMessage("There are No Properties...");
	return;
}
PO = First(*PropertiesList);
ShowProperty(*PropertiesList,PropertiesCount,PO,ind,1);
do
{
ch=getch();
if (ch==0,ch==75)
	ind--;
if (ch==0,ch==77)
	ind++;

if (ind >=*PropertiesCount )
	ind=0;
if (ind<0)
	ind = *PropertiesCount-1;

if (ch=='U' || ch=='u')
	UpdateByIndex(*PropertiesList,ind);

if (ch=='E' || ch=='e')
{
	DeleteByIndex(*PropertiesList,PropertiesCount,ind);
	if (ind==*PropertiesCount)
		ind--;
	if(*PropertiesCount==0)
		break;
}

PO = AdvanceEx(First(*PropertiesList),ind);
if ((ch==0,ch==75) || (ch==0,ch==77) || ch=='u' || ch=='U' || ch=='E' || ch=='e')
ShowProperty(*PropertiesList,PropertiesCount,PO,ind,1);


}
while (ch!=27);


}
//Function Show one page per property and all property info.
void ShowProperty(List PropertiesList,int *PropertiesCount,Position P,int ind,int Options)
{
DemandProperty D;
char ch;

D = Retrieve(P);

showoutputimage(0);

if (ind!=-1)
	putnumber(ind+1,5,2,0x0a,0,0,"[","]");

puttext("P r o p e r t y",25,2,0x0b,1,0);
puttext("(Use Arrow Keys To Navigate) ",37,2,0x0e,0,0);
fillascii(3,3,76,3,'-',0x0b);

puttext("Property City: ",5,5,0x0a,0,0);
puttext(D.PropertyRecord.PropertyCity,5+20,5,0x0d,0,0);

puttext("Property Kind: ",5,7,0x0a,0,0);
puttext(DPKindStrings[D.PropertyRecord.PropertyKind],5+20,7,0x0d,0,0);

puttext("Property Type: ",5,9,0x0a,0,0);
puttext(DPPropertyTypeStrings[D.PropertyRecord.PropertyType],5+20,9,0x0d,0,0);

puttext("Property Price: ",5,11,0x0a,0,0);
putnumber(D.PropertyRecord.PropertyPrice,5+20,11,0x0d,0,0,NULL,"$");

puttext("Property Rooms: ",5,13,0x0a,0,0);
putnumber(D.PropertyRecord.PropertyRooms,5+20,13,0x0d,0,0,NULL,NULL);

puttext("Contacts",40,16,0x0b,1,0);
fillascii(3,17,76,17,'-',0x0b);

puttext("Contact 1 Name: ",5,18,0x0a,0,0);
puttext(D.PropertyRecord.Contacts[0].FullName,5+20,18,0x0d,0,0);

puttext("Contact 1 Phone: ",5,19,0x0a,0,0);
puttext(D.PropertyRecord.Contacts[0].Phone,5+20,19,0x0d,0,0);

puttext("Contact 2 Name: ",5,21,0x0a,0,0);
puttext(D.PropertyRecord.Contacts[1].FullName,5+20,21,0x0d,0,0);

puttext("Contact 2 Phone: ",5,22,0x0a,0,0);
puttext(D.PropertyRecord.Contacts[1].Phone,5+20,22,0x0d,0,0);

ShowOptions();

if (D.PropertyRecord.PropertyActive==1)
	GrayScreen(128); // gray screen if disabled.

if (!Options)
{	ch=getch();
	if(ch=='u' || ch=='U')
		UpdateByIndex(PropertiesList,ind);
	if(ch=='E' || ch=='e')
		DeleteByIndex(PropertiesList,PropertiesCount,ind);	
}
	

}
//Recursive write the properties to file.
void WriteProperty(FILE *fp,Position PO)
{
DemandProperty D;
if (Advance(PO)!=NULL)
	WriteProperty(fp,Advance(PO));

	D = Retrieve(PO);
	fwrite(&(D.PropertyRecord),sizeof(Property),1,fp);

}
//Main function to write properties, call the recursive function.
void WritePropertiesToFile(List PropertiesList,int *PropertiesCount)
{
FILE *fp;

fp = fopen(PropertiesFile,"wb+");
if (fp==NULL)
{
	printf("Error Writing to file...");
	exit(1);
}
if (*PropertiesCount>0)
	WriteProperty(fp,First(PropertiesList));
fclose(fp);
}
//Load properties from the file.
List LoadPropertiesFromFile(int *PropertiesCount)
{
FILE *fp;
int records=0,i;
DemandProperty *DP;
List PropertiesList;
Property P;

fp = fopen(PropertiesFile,"rb+");
if (fp==NULL)
{
	//printf("Erroor Writing to file...");
	//exit(1);
	return NULL;
}
fseek(fp,0,SEEK_END);
records = ftell(fp) / sizeof(Property);
fseek(fp,0,SEEK_SET);

PropertiesList = MakeEmpty(NULL);
for(i=0;i<records;i++)
{
	fread(&P,sizeof(Property),1,fp);
	DP = (DemandProperty*)malloc(sizeof(DemandProperty));
	memcpy(&(DP->PropertyRecord),&P,sizeof(Property));
	Insert(*DP,PropertiesList,Header(PropertiesList),EProperty);
	(*PropertiesCount)++;
}
fclose(fp);
return PropertiesList;
}
//Free allocations on exit.
void FreePropertiesAllocationsBeforeExit(List PropertiesList,int *PropertiesCount)
{
	if (*PropertiesCount>0)
	{
		DeleteList(PropertiesList);
		PropertiesList = NULL;
	}
}
//Shows menu of properties.user select one using toggled menu,return index.
int SelectPropertyFromUser(List PropertiesList,int *PropertiesCount)
{
char **arr;
Position PO;
DemandProperty D;
int i=0,j,pid=0;
arr = (char **)malloc((*PropertiesCount) * (sizeof(char *)));
PO = First(PropertiesList);
while(PO)
{
	D = Retrieve(PO);
	arr[i] = (char*)malloc(sizeof(char)*100);
	//fill info to array arr
	FillPropertyInfo(&D,&arr[i],0,i+1);

	i++;
	PO = Advance(PO);
}
	//show menu
	pid = menu(arr,i,0,"Select Property",5);

	for(j=9;j<i-1;j++)
		free(arr[j]);
	free(arr);
	
	return pid;

}
//select property from user,start update.
void Update_Property(List *PropertiesList,int *PropertiesCount)
{
int pid;
if (*PropertiesCount==0)
{
	ConfirmationMessage("There are No Properties...");
	return;
}
pid = SelectPropertyFromUser(*PropertiesList,PropertiesCount);
if (pid==-1)
	return;

UpdateByIndex(*PropertiesList,pid);

}
//Function receive property, asks user to give field and update the field.
void UpdatePropertyField(DemandProperty *D)
{
int fieldid;

fieldid = menu(PropertiesFields,PropertiesFieldsLength,0,"Select Field",12);
if (fieldid==-1)
	return;

switch (fieldid)
{
case 0:Client_Insert_Between(&(D->PropertyRecord.PropertyKind),DPKindStrings,DPKindLength,"Select Kind:");break;
case 1:Client_Insert_Between(&(D->PropertyRecord.PropertyType),DPPropertyTypeStrings,ActiveStringsLength,"Select Type:");break;
case 2:Client_Insert_String(D->PropertyRecord.PropertyCity,"Enter City: ",20,TEXT_S);break;
case 3:Client_Insert_Byte(&(D->PropertyRecord.PropertyRooms),2,"Enter Rooms: ");break;
case 4:Client_Insert_Integer(&(D->PropertyRecord.PropertyPrice),9,"Enter Price: ");break;
case 5:Client_Insert_Between(&D->PropertyRecord.PropertyActive,ActiveStrings,ActiveStringsLength,"Select Property Activation:");break;
case 6:Client_Insert_String(D->PropertyRecord.Contacts[0].FullName,"Enter Contact 1 Name: ",20,TEXT_S);break;
case 7:Client_Insert_String(D->PropertyRecord.Contacts[0].Phone,"Enter Contact 1 Phone: ",20,NUM_S);break;		
case 8:Client_Insert_String(D->PropertyRecord.Contacts[1].FullName,"Enter Contact 2 Name: ",20,TEXT_S);break;
case 9:Client_Insert_String(D->PropertyRecord.Contacts[1].Phone,"Enter Contact 2 Phone: ",20,NUM_S);break;		
}

}
//Function find the property by index, and start to update by field.
void UpdateByIndex(List PropertiesList,int ind)
{
Position PO;
DemandProperty *D;
PO = AdvanceEx(First(PropertiesList),ind);
D = RetrieveAdr(PO);
UpdatePropertyField(D);
}
//Function delete property by index
void DeleteByIndex(List PropertiesList,int *PropertiesCount,int ind)
{
	if (AskYesNoQuestion("Are You Sure ?"))
	{
		DeleteInd(ind,PropertiesList);
		(*PropertiesCount)--;
		if (*PropertiesCount==0)
			DeleteList(PropertiesList);
	}
}
//Function asks user to select property,then delete it by index. 
void Delete_Property(List *PropertiesList,int *PropertiesCount)
{
int pid;
if (*PropertiesCount==0)
{
	ConfirmationMessage("There are No Properties...");
	return;
}
showoutputimage(0);
pid = SelectPropertyFromUser(*PropertiesList,PropertiesCount);
if (pid==-1)
	return;

if (AskYesNoQuestion("Are You Sure?"))
{
	DeleteInd(pid,*PropertiesList);
	(*PropertiesCount)--;
}

}
//Function asks user the search type of property (by city,by kind)
//then shows the results in list, user can select the line and see info.
void Find_Property(List *PropertiesList,int *PropertiesCount)
{
//struct of line's result
typedef struct
{
	Position P; //position of the struct
	int ind;    //index in the list
}P_RES;
char **Arr=NULL,S_BUF[30];
P_RES *Results=NULL; //results array
Position PO;
DemandProperty D;
int pid,fieldid=0,ind=0;
byte I_BUF=0,addtolist=0,listcount=0;

if (*PropertiesCount==0)
{
	ConfirmationMessage("There are no properties in the system");
	return;
}

fieldid = menu(PropertiesFieldsSearch,ActiveStringsLength,0,"Search",12);
if (fieldid==-1)
	return;

switch (fieldid)
{
case 0: Client_Insert_String(S_BUF,"Enter City: ",20,TEXT_S);break;
case 1:	Client_Insert_Between(&I_BUF,DPKindStrings,DPKindLength,"Select Kind:");break;
}

PO = First(*PropertiesList);
while (PO)
{
	addtolist = 0;
	D = Retrieve(PO);
	switch (fieldid)
	{
		//if found addtolist = true
		case 0: if (strcmp(D.PropertyRecord.PropertyCity,S_BUF)==0)
						addtolist=1;
							break;
		case 1: if (D.PropertyRecord.PropertyKind==I_BUF)
						addtolist=1;
							break;
	}

	if (addtolist && D.PropertyRecord.PropertyActive==0)
	{   //update the results array, create the results string array.
		listcount++;
		Results = (P_RES*)realloc(Results,sizeof(P_RES)*listcount);
		Results[listcount-1].P = PO;
		Results[listcount-1].ind = ind;
		Arr = (char**)realloc(Arr,sizeof(char*)*listcount);
		Arr[listcount-1] = malloc(sizeof(char)*100);
		FillPropertyInfo(&D,&Arr[listcount-1],0,listcount);
	}

	PO = Advance(PO);
	ind++;
}

if (listcount==0)
{
	ConfirmationMessage("Properties Not Found");
	return;
}
pid = menu(Arr,listcount,0,"Results",12);

//on user select, retrieve from the results array the real struct, according to the index.
if (pid!=-1)
	ShowProperty(*PropertiesList,PropertiesCount,Results[pid].P,Results[pid].ind,0);

for(pid=0;pid<listcount;pid++)
	free(Arr[pid]);

free(Arr);
free(Results);

}
//update string with properties info. full = 0\1(additional info)
void FillPropertyInfo(DemandProperty *D,char **BUF,int full,int ind)
{
	if (full)
	{
	sprintf(*BUF,"%s, %s, %s, %d, %d$, %s-%s,%s-%s.",D->PropertyRecord.PropertyCity,
	DPKindStrings[D->PropertyRecord.PropertyKind],
	DPPropertyTypeStrings[D->PropertyRecord.PropertyType],
	D->PropertyRecord.PropertyRooms,D->PropertyRecord.PropertyPrice,
	D->PropertyRecord.Contacts[0].FullName,D->PropertyRecord.Contacts[0].Phone,
	D->PropertyRecord.Contacts[1].FullName,D->PropertyRecord.Contacts[1].Phone);	
	}
	else
	{
	sprintf(*BUF,"[%d]. %s, %s, %s, %d, %d$.",ind,D->PropertyRecord.PropertyCity,
	DPKindStrings[D->PropertyRecord.PropertyKind],
	DPPropertyTypeStrings[D->PropertyRecord.PropertyType],
	D->PropertyRecord.PropertyRooms,D->PropertyRecord.PropertyPrice);	
	}
}

//Print options.
void ShowOptions()
{
puttext("Press (E)rase,(U)pdate",55,23,0x1f,0,0);
puttext("E",62,23,0x1a,0,0);
puttext("U",70,23,0x1a,0,0);
}
//Asks user for filename, create the file and fill it with infos of the properties.
void Show_Reports_Properties_Text_File(List PropertiesList,int *PropertiesCount)
{
FILE *fp;
char FN[20],*BUF;
Position PO;
DemandProperty D;

if (*PropertiesCount==0)
{
	ConfirmationMessage("There are no properties in the system");
	return;
}

if (!Client_Insert_String(FN,"Enter File Name: ",15,TEXT_S))
	return;
fp = fopen(FN,"w");

PO = First(PropertiesList);
while (PO)
{
	D = Retrieve(PO);
	BUF = (char*)malloc(sizeof(char)*100);
	FillPropertyInfo(&D,&BUF,1,0);
	fprintf(fp,BUF);
	fprintf(fp,"\n");
	free(BUF);
	PO = Advance(PO);
}

fclose(fp);

BUF = (char*)malloc(sizeof(char)*100);
sprintf(BUF,"Text File [%s] Created.",FN);
ConfirmationMessage(BUF);
free(BUF);

system(FN);
}

//Asks to insert contact name, then creates file of the properties
//contain that contact name.
void Show_Reports_Properties_By_Name_Text_File(List PropertiesList,int *PropertiesCount)
{
FILE *fp;
char UN[20],FN[20],*BUF;
Position PO;
DemandProperty D;

if (*PropertiesCount==0)
{
	ConfirmationMessage("There are no properties in the system");
	return;
}

if (!Client_Insert_String(UN,"Enter contact Name: ",20,TEXT_S))
	return;

if (!Client_Insert_String(FN,"Enter File Name: ",15,TEXT_S))
	return;

fp = fopen(FN,"w");

PO = First(PropertiesList);
while (PO)
{
	D = Retrieve(PO);
	BUF = (char*)malloc(sizeof(char)*100);
	FillPropertyInfo(&D,&BUF,1,0);
	if ((strcmp(UN,D.PropertyRecord.Contacts[0].FullName)==0)
		||
		(strcmp(UN,D.PropertyRecord.Contacts[1].FullName)==0))
	{
		fprintf(fp,BUF);
		fprintf(fp,"\n");
	}
	free(BUF);
	PO = Advance(PO);
}

fclose(fp);

BUF = (char*)malloc(sizeof(char)*100);
sprintf(BUF,"Text File [%s] Created.",FN);
ConfirmationMessage(BUF);
free(BUF);

system(FN);
}

